#include "WSceneRenderer.h"

#include "BindingPt.h"
#include "WQuad.h"
#include "Axis.h"

#include "GUIShader.h"

#include "RTSCamera.h"
#include "glm\gtc\matrix_transform.hpp"

void WSceneRenderer::Init()
{
	InitAntTweakGUI();
	WRenderer::Init();

	renderTexture = new RenderTexture();
	renderTexture->Init(1, 1920, 1080, RenderTexture::DepthBuffer);

	InitSkyBox();
	InitAxis();
	InitGUI();
	InitIrradianceBaker();
	
	AddTextureDisplay(0, 300, renderTexture->GetTextureID());
	//register texture enabled pointer to gui
	enableTextureDisplay = true;
	TwAddVarRW(antTweakBar.get(), "Texture.Enable", TW_TYPE_BOOL32, &enableTextureDisplay, 
               " label='Texture.Enable' key=space help='Toggle auto-rotate mode.' ");

	//load example scene
	std::shared_ptr<WLoadItem> item = std::shared_ptr<WLoadItem>(new WLoadItem(std::string(".\\Data\\Monkey\\monkey.dae")));
	std::shared_ptr<SceneNode> newNode = nullptr;
	
	bool success = Import3DFromFile(item->GetPath().c_str(), newNode);
	if(success)
	{

	}
}

void WSceneRenderer::InitSkyBox()
{
	
	/****************************************************************************************
	**									load skybox
	*****************************************************************************************/
	//load skybox
	std::vector<std::string> imagePaths;
	imagePaths.push_back(".\\Data\\SkyBox\\Skybox1\\Skybox-Right.bmp");
	imagePaths.push_back(".\\Data\\SkyBox\\Skybox1\\Skybox-Left.bmp");

	imagePaths.push_back(".\\Data\\SkyBox\\Skybox1\\Skybox-Front.bmp");
	imagePaths.push_back(".\\Data\\SkyBox\\Skybox1\\Skybox-Back.bmp");

	imagePaths.push_back(".\\Data\\SkyBox\\Skybox1\\Skybox-Top.bmp");
	imagePaths.push_back(".\\Data\\SkyBox\\Skybox1\\Skybox-Bottom.bmp");

	std::vector<GLuint> faces;
	faces.push_back(GL_TEXTURE_CUBE_MAP_POSITIVE_X);
	faces.push_back(GL_TEXTURE_CUBE_MAP_NEGATIVE_X);

	faces.push_back(GL_TEXTURE_CUBE_MAP_POSITIVE_Z);
	faces.push_back(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z);

	faces.push_back(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y);
	faces.push_back(GL_TEXTURE_CUBE_MAP_POSITIVE_Y);

	skyBox = std::shared_ptr<TextureCubeMap>(new TextureCubeMap(imagePaths, faces));
	skyBox->LoadImage();
				
	//init skybox material
	std::shared_ptr<Shader> shader = std::shared_ptr<Shader>(Shader::GetShader("SkyBoxShader"));
	shader->AddParameter(Shader::Diffuse, skyBox);
	std::shared_ptr<Material> material = std::shared_ptr<Material>(new Material("SkyBoxMat", false, false, shader));
	Material::AddMaterial(std::string("SkyBoxMat"), material);

	//init everything else
	skyBoxNode = std::shared_ptr<SceneNode>(new SceneNode("TestCube"));
	std::shared_ptr<WCube> cube = std::shared_ptr<WCube>(new WCube("SkyBoxMat"));
	cube->GenVBO();
	skyBoxNode->AddMesh(cube);
	cube->SetParent(skyBoxNode);
}

void WSceneRenderer::InitAxis()
{
	/****************************************************************************************
	**									load axes
	*****************************************************************************************/
	std::shared_ptr<Shader> shader = std::shared_ptr<Shader>(Shader::GetShader("ColorShader"));
	std::shared_ptr<Material> material = std::shared_ptr<Material>(new Material("AxisMaterial", false, false, shader));
	Material::AddMaterial(std::string("AxisMaterial"), material);

	std::vector<glm::vec3> xAxisPts;
	xAxisPts.push_back(glm::vec3(0, 0, 0));
	xAxisPts.push_back(glm::vec3(100, 0, 0));
	xAxisPts.push_back(glm::vec3(0, 0, 0));
	xAxisPts.push_back(glm::vec3(0, 100, 0));
	xAxisPts.push_back(glm::vec3(0, 0, 0));
	xAxisPts.push_back(glm::vec3(0, 0, 100));
	
	std::vector<glm::vec4> xAxisColor;
	xAxisColor.push_back(glm::vec4(1, 0, 0, 1));
	xAxisColor.push_back(glm::vec4(1, 0, 0, 1));
	xAxisColor.push_back(glm::vec4(0, 1, 0, 1));
	xAxisColor.push_back(glm::vec4(0, 1, 0, 1));
	xAxisColor.push_back(glm::vec4(0, 0, 1, 1));
	xAxisColor.push_back(glm::vec4(0, 0, 1, 1));

	std::shared_ptr<Axis> xAxis = std::make_shared<Axis>(xAxisPts, xAxisColor, std::string("AxisMaterial"));
	xAxis->GenVBO();

	axis = std::make_shared<SceneNode>("Axis");
	axis->AddMesh(xAxis);
	xAxis->SetParent(axis);
	
	std::shared_ptr<SceneGraph> defaultSceneGraph = std::shared_ptr<SceneGraph>(SceneGraph::GetDefaultSceneGraph());
	std::shared_ptr<SceneNode> root = std::shared_ptr<SceneNode>(defaultSceneGraph->GetRoot());

	root->AddChild(axis);
}


void WSceneRenderer::InitGUI()
{
	guiNode = std::make_shared<SceneNode>("GUI");

	//GUI projection matrix and GUI camera
	guiMatrix = glm::ortho(0.0f, (float) WINDOW_WIDTH, 0.0f, (float) WINDOW_HEIGHT);
	gParams.viewProjMat = guiMatrix;

	//generate the one-per-scene uniform buffer object, we don't do per frame
	//update for this since the ortho matrix doesn't change (for now)
	glGenBuffers(1, &guiUBO);
	glBindBuffer(GL_UNIFORM_BUFFER, guiUBO);

	glBufferData(GL_UNIFORM_BUFFER, sizeof(gParams), &gParams, GL_DYNAMIC_DRAW);
	glBindBufferBase(GL_UNIFORM_BUFFER, GUI_PARAM, guiUBO);
	glBindBuffer(GL_UNIFORM_BUFFER, 0);
}

void WSceneRenderer::AddTextureDisplay(float bottomLeft, float topRight, GLuint textureID)
{
	/********************************************************************************
	**							Create gui quad
	*********************************************************************************/
	std::vector<glm::vec3> pts;
	pts.push_back(glm::vec3(bottomLeft, bottomLeft, 0));
	pts.push_back(glm::vec3(topRight, topRight, 0));
	pts.push_back(glm::vec3(bottomLeft, topRight, 0));
	pts.push_back(glm::vec3(topRight, topRight, 0));
	pts.push_back(glm::vec3(bottomLeft, bottomLeft, 0));
	pts.push_back(glm::vec3(topRight, bottomLeft, 0));
	
	std::vector<glm::vec2> uvs;
	uvs.push_back(glm::vec2(0, 0));
	uvs.push_back(glm::vec2(1, 1));
	uvs.push_back(glm::vec2(0, 1));
	uvs.push_back(glm::vec2(1, 1));
	uvs.push_back(glm::vec2(0, 0));
	uvs.push_back(glm::vec2(1, 0));

	std::shared_ptr<Shader> shader = std::shared_ptr<Shader>(Shader::GetShader("GUIShader"));
	shader->AddParameter(Shader::Diffuse, textureID);

	std::shared_ptr<Material> material = std::shared_ptr<Material>(new Material("GUIMat", false, false, shader));
	Material::AddMaterial(std::string("GUIMat"), material);

	std::shared_ptr<WQuad> testQuad = std::make_shared<WQuad>(pts, uvs, std::string("GUIMat"));
	testQuad->GenVBO();
	guiNode->AddMesh(testQuad);

}

void WSceneRenderer::InitAntTweakGUI()
{
	TwInit(TW_OPENGL_CORE, nullptr);

	antTweakBar = std::shared_ptr<TwBar>(TwNewBar("Parameters"));
	TwWindowSize(WINDOW_WIDTH, WINDOW_HEIGHT);
}


void WSceneRenderer::InitIrradianceBaker()
{
	irradianceBaker = std::make_shared<WIrradianceBaker>(skyBox);
}

void WSceneRenderer::PollInput() 
{
	sf::Event event;

	while(window->pollEvent(event))
	{
		int twEventHandled = TwEventSFML(&event, 1, 6);
		if(!twEventHandled)
		{
			if (event.type == sf::Event::Closed)
			{
				// end the program
				running = false;
			}
			else if(event.type == sf::Event::Resized)
			{
				glViewport(0, 0, event.size.width, event.size.height);
			}

			PollMouseInput(event);
		}
	}
}

void WSceneRenderer::Render()
{
	Pass = WRenderer::NORMAL;

	UpdateUBOForRender();
	
	std::shared_ptr<SceneGraph> defaultSceneGraph = std::shared_ptr<SceneGraph>(SceneGraph::GetDefaultSceneGraph());
	std::shared_ptr<SceneNode> root = std::shared_ptr<SceneNode>(defaultSceneGraph->GetRoot());

	renderTexture->Bind();
	skyBoxNode->Render(Pass);
	root->Render(Pass);
	renderTexture->Unbind();

	if(enableTextureDisplay)
	{
		if(guiNode != nullptr) guiNode->Render(Pass);
	}

	//render ant tweak gui at the end
	TwDraw();
}
